using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;


namespace CNGraphi.U6URP.Editor
{
    /// <summary>
    /// 着色器通用检视板
    /// <para>适用于非 ShaderGraph 蓝图编写的着色器（手工编写）</para>
    /// <para>作者：CN.Graphi</para>
    /// </summary>
    public class ShaderManualGUI : ShaderGUI
    {
        #region 自定义Drawer
        // 纹理单行显示
        internal class SingleLineDrawer : MaterialPropertyDrawer
        {
            public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
            {
                editor.TexturePropertySingleLine(label, prop);
            }
            public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
            {
                return 0;
            }
        }
        //自定义 Foldout 显示效果
        internal class FoldoutDrawer : MaterialPropertyDrawer
        {
            bool show = true;
            public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
            {
                // 用于第一次打开材质面板，以 Shader 文件属性的 Foldout 值来进行折叠显示
                show = (prop.floatValue == 1) ? true : false;
                // 用于手动控制 Foldout 折叠
                Color c = GUI.backgroundColor;
                GUI.backgroundColor = Color.HSVToRGB(0.1f, 0.1f, 1.0f);
                show = EditorGUILayout.Foldout(show, label, true, new GUIStyle("OL Title") { contentOffset =new Vector2(5,0), margin = new RectOffset(10, 0, 0, 0)});
                GUI.backgroundColor = c;
                EditorGUILayout.Space(10);

                // 更新值
                prop.floatValue = Convert.ToSingle(show);
            }
            public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
            {
                return 0;
            }
        }
        // 标题、描述
        internal class DescDrawer : MaterialPropertyDrawer
        {
            readonly bool isWeight;
            readonly int indent;
            public DescDrawer() : this("_0", "true"){ }
            public DescDrawer(string indent, string weight) 
            {
                this.indent = int.Parse(indent.Substring(1));
                isWeight = bool.Parse(weight); 
            }
            public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
            {
                string msg = (isWeight) ? $"<b>{prop.displayName}</b>" : prop.displayName;
                EditorGUI.indentLevel += indent;
                EditorGUILayout.LabelField(msg, new GUIStyle("minilabel") { richText = true, fontSize = 12 });
                EditorGUI.indentLevel -= indent;
            }
            public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
            {
                return 0;
            }
        }
        // 缩进
        internal class IndentDrawer : MaterialPropertyDrawer
        {
            private readonly int mIndentValue;
            public IndentDrawer() : this("_1") { } 
            public IndentDrawer(string value) 
            {
                mIndentValue = int.Parse(value.Substring(1));
            }
            public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
            {
                EditorGUI.indentLevel += mIndentValue;
                editor.DefaultShaderProperty(prop, label.text);
                EditorGUI.indentLevel -= mIndentValue;
            }
            public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
            {
                return 0;
            }
        }
        #endregion



        #region 变量
        public class MaterialData
        {
            public MaterialProperty m_prop; //材质属性
            public int m_indentLevel = 0; //属性显示时是否进行缩进
            public int m_childIndentLv = 0; //属性中子属性是否需要缩进
            public bool m_disabled = false; //属性是否可操作
            public string m_helpdesc = null; //属性说明
            public string m_foldoutName = null; // Foldout 名称
            public Action m_drawOnLater = null; // 在属性后绘制额外的GUI
        }

        protected Dictionary<string, MaterialProperty> m_MaterialProperty = new Dictionary<string, MaterialProperty>(); //存储材质中所有属性
        protected List<MaterialData> m_MaterialDataList = new List<MaterialData>(); //存储自定义材质数据，自定义数据包含材质属性及显示是否缩进信息（但这里不包含 Foldout 为关闭状态时，其子节点的显示）
        #endregion



        /// <summary>
        /// 检测属性是否在着色器属性显示列表内
        /// </summary>
        /// <param name="propname"></param>
        /// <returns></returns>
        protected MaterialData InMaterialDataList(string propname)
        {
            MaterialData data;
            for (int i = 0; i < m_MaterialDataList.Count; i++)
            {
                data = m_MaterialDataList[i];
                if (data.m_prop.name == propname) { return data; }
            }
            return null;
        }


        /// <summary>
        /// 获取属性对象
        /// </summary>
        /// <param name="propname"></param>
        /// <returns></returns>
        protected MaterialProperty GetProperty(string propname)
        {
            if (string.IsNullOrEmpty(propname)) { return null; }
            m_MaterialProperty.TryGetValue(propname, out MaterialProperty materialProperty);
            return materialProperty;
        }


        /// <summary>
        /// 针对可在 Inspector 面板中显示的着色器属性进一步筛选（此函数会在基类筛选操作完毕后执行。子类重写）
        /// </summary>
        /// <param name="materialEditor"></param>
        /// <param name="properties"></param>
        protected virtual void FurtherSelect(MaterialEditor materialEditor, MaterialProperty[] properties) { }


        /// <summary>
        /// 设置关键字
        /// </summary>
        /// <param name="mat"></param>
        protected virtual void SetKeywords(Material mat) { }



        /// <summary>
        /// GUI
        /// </summary>
        /// <param name="materialEditor"></param>
        /// <param name="properties"></param>
        public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
        {
            m_MaterialDataList.Clear();
            m_MaterialProperty.Clear();

            //组织数据
            Material thisMat = materialEditor.target as Material;
            Shader shader = thisMat.shader;
            for (int i = 0; i < properties.Length; i++)
            {
                var propertie = properties[i];
                m_MaterialProperty[propertie.name] = propertie;
                m_MaterialDataList.Add(new MaterialData() { m_prop = propertie, m_indentLevel = 0 });
                var attrs = shader.GetPropertyAttributes(i);
                foreach (var item in attrs)
                {
                    if (item.StartsWith("To"))
                    {
                        Match match = Regex.Match(item, @"(\w+)\s*\((.*)\)"); //匹配格式：(\w+)字母、下划线、字符1个或多个，\s* 匹配空格0个或多个， \((.*)\) 匹配任意字符0个或多个
                        if (match.Success)
                        {
                            var name = match.Groups[2].Value.Trim();
                            int inx = m_MaterialDataList.Count - 1;
                            m_MaterialDataList[inx].m_foldoutName = name;
                            if (m_MaterialProperty.TryGetValue(name, out var a))
                            {
                                if (a.floatValue == 0f)
                                {
                                    m_MaterialDataList.RemoveAt(inx);
                                    break;
                                }
                                else 
                                {
                                    m_MaterialDataList[inx].m_indentLevel = 1;
                                }
                            }
                        }
                    }
                }
            }

            //进一步筛选
            FurtherSelect(materialEditor, properties);

            //绘制
            EditorGUILayout.BeginVertical("helpbox");

            Color defaultC = GUI.backgroundColor;
            GUI.backgroundColor = Color.HSVToRGB(0.1f,0.1f,0.2f,true);
            var sha = (materialEditor.target as Material).shader;
            GUILayout.Box($"<color=#d8d8d8><b><color=#e8e8e8><size=15>{(materialEditor.target as Material).shader.name}</size></color></b>\n<size=11>Pass: {sha.passCount} SubShader: {sha.subshaderCount}</size></color>", new GUIStyle("OverrideMargin") { richText = true, fontSize = 17, contentOffset = new Vector2(10, 3) }, GUILayout.Height(50));
            GUI.backgroundColor = defaultC;

            //说明
            DrawHelpDesc();  
            EditorGUILayout.Space(10);


            MaterialData md;
            for (int i = 0; i < m_MaterialDataList.Count; i++)
            {
                md = m_MaterialDataList[i];
                MaterialProperty prop = md.m_prop; //属性
                int indentLevel = md.m_indentLevel; //是否需要缩进
                int childIndentlv = md.m_childIndentLv; //子元素是否需要缩进
                bool disabled = md.m_disabled; //属性是否可操作
                string helpdesc = md.m_helpdesc; //帮助说明
                Action drawOnLater = md.m_drawOnLater; //在属性绘制后再绘制额外的GUI

                if ((prop.flags & (MaterialProperty.PropFlags.HideInInspector | MaterialProperty.PropFlags.PerRendererData)) == MaterialProperty.PropFlags.None) //HideInInspector 和 PerRendererData 标记的属性不进行绘制
                {
                    float propertyHeight = materialEditor.GetPropertyHeight(prop, prop.displayName);
                    Rect controlRect = EditorGUILayout.GetControlRect(true, propertyHeight, EditorStyles.layerMaskField);

                    if (indentLevel != 0) EditorGUI.indentLevel += indentLevel;
                    if (childIndentlv != 0) EditorGUI.indentLevel += childIndentlv;

                    //属性
                    EditorGUI.BeginDisabledGroup(disabled);
                    materialEditor.ShaderProperty(controlRect, prop, prop.displayName); //绘制属性项
                    drawOnLater?.Invoke();// 绘制额外属性项
                    EditorGUI.EndDisabledGroup();

                    //属性对应的帮助说明显示
                    if (!string.IsNullOrEmpty(helpdesc))
                    {
                        Gui.Tip(helpdesc);
                    }

                    if (childIndentlv != 0) EditorGUI.indentLevel -= childIndentlv;
                    if (indentLevel != 0) EditorGUI.indentLevel -= indentLevel;
                }
            }

            // 设置关键字信息
            SetKeywords(thisMat);

            //Unity 材质默认项
            EditorGUILayout.Space(20);
            EditorGUILayout.BeginVertical("box");
            if (SupportedRenderingFeatures.active.editableMaterialRenderQueue)
            {//如果支持渲染功能中渲染队列的动态设置，则显示。
                materialEditor.RenderQueueField();
            }
            //显示GPU实例操作项
            materialEditor.EnableInstancingField();
            //显示双边GI操作项
            materialEditor.DoubleSidedGIField();
            EditorGUILayout.EndVertical();

            EditorGUILayout.EndVertical();
        }




        protected Vector2 m_v2 = Vector2.zero;
        /// <summary>
        /// 着色器说明信息
        /// </summary>
        /// <param name="desc"></param>
        /// <param name="h"></param>
        protected virtual void DrawHelpDesc(string desc = "", int h = 80)
        {
            if (string.IsNullOrEmpty(desc)) { return; }
            EditorGUILayout.Space(10);
            m_v2 = EditorGUILayout.BeginScrollView(m_v2, GUILayout.Height(h));
            EditorGUILayout.LabelField(desc, new GUIStyle("helpbox") { alignment = TextAnchor.UpperLeft, fontSize = 11, richText = true });
            EditorGUILayout.EndScrollView();
        }
    }
}